import ctypes
import tkinter as tk
import tkinter.ttk as ttk
import os
import tkinter.messagebox as tkm
import tkinter.simpledialog as tksd
import sys
import pathlib
import shutil
from importlib import import_module
import json
from packaging.version import Version
from watchdog.events import FileSystemEventHandler, EVENT_TYPE_OPENED, EVENT_TYPE_CLOSED
from watchdog.observers import Observer

import Tookits
from Widget import ControlPad
from views import BaseView

DEBUG = "On branch dev" in os.popen("git status").read()
print(DEBUG)
appPATH = os.path.dirname(sys.argv[0])
print("程序路经：", appPATH)
# if DEBUG:
#     print("DEBUG...")
#     # config_dir = os.path.join(appPATH, ".artnote")
# else:
#     config_dir = os.path.join(pathlib.Path.home(), ".artnote")
config_dir = os.path.join(appPATH, ".artnote")
config_file = os.path.join(config_dir, "artnote.json")
if not os.path.isdir(config_dir):
    print("安装中...")
    print("%s --> %s" % (os.path.join(appPATH, ".artnote"), config_dir))
    shutil.copytree(os.path.join(appPATH, ".artnote"), config_dir)
if not os.path.isfile(config_file):
    with open(config_file, mode='w') as file:
        file.write('')

__Version__ = os.popen("git describe --abbrev=0 --tags").read()[:-1]
__Data__ = os.popen('git log --tags --simplify-by-decoration --pretty="format:%cd %d" --date=format:"%Y-%m-%d"')\
    .read().split("\n")[0].split(' ')[0]
__URL__ = "https://gitee.com/ysu-xzh/art-note/"

FOLDER_FILE = '.art'  # 用于存放目录文件。
support_file_list = ['.txt', '.md']


class Application:
    plate = tk.Tk()
    pan = tk.PanedWindow(plate, orient=tk.HORIZONTAL)  # 'horizontal'

    frame = tk.Frame(pan)
    file_tree = ttk.Treeview(frame)
    file_tree.tag_configure("font", font=("微软雅黑", 12))
    scrollbal1 = tk.Scrollbar(frame)

    style = ttk.Style()
    style.layout("TNotebook.Tab", [])
    # https://coderslegacy.com/python/hide-tkinter-ttk-notebook-tabs/
    unionview = ttk.Notebook(plate)


    controlpad = ControlPad(plate, redirection=not DEBUG)  # 命令行部件.

    IsTop, IsRowText, IsHideTree = tk.IntVar(), tk.BooleanVar(), tk.IntVar(value=False)  # value=(mode=='file')

    # 文件搜寻
    father_of_now = list([''])
    tree_num = list([0])
    tree_path = dict()

    file_path = ''
    file_name = ''

    class FileEventHandler(FileSystemEventHandler):
        def __init__(self, delegetor):
            self.delegetor = delegetor
            super().__init__()
        def on_any_event(self, event):
            if event.event_type not in (EVENT_TYPE_OPENED, EVENT_TYPE_CLOSED):
                if ".git" not in event.src_path:
                    print(event)
                    self.delegetor.update()


    def __init__(self, config_path):
        self.plate.protocol("WM_DELETE_WINDOW", self.on_closing)
        # 配置信息
        self.config_path = config_path
        with open(config_file, "r", encoding="utf-8") as file:
            try:
                config = json.load(file)
            except json.decoder.JSONDecodeError:
                config = {}
        config = Tookits.ConfigToolkits.checkconfig(config)
        self.config = config
        self.openedfile = self.config['filetree']['openedfile']
        self.ignoresufix = self.config['load']['ignoresuffix']

        self.img_file = None
        self.img_folder = None
        config_dir_, _ = os.path.split(self.config_path)
        if os.path.isfile(os.path.join(config_dir_, 'file.png')):
            self.img_file = tk.PhotoImage(file=os.path.join(config_dir_, 'file.png'))
        if os.path.isfile(os.path.join(config_dir_, 'folder.png')):
            self.img_folder = tk.PhotoImage(file=os.path.join(config_dir_, 'folder.png'))
        if os.path.isfile(os.path.join(config_dir_, 'app.ico')):
            self.plate.iconbitmap(os.path.join(config_dir_, 'app.ico'))
        # 载入笔记
        self.git = Tookits.GitManager(repo_path=self.config['app']['notebook'], repo_url=self.config['app']['url'])
        print(self.git.download())
        # 看门狗  会把临时文件全都检测到，不好用。还得写规则，先不用了。
        # self.observer = Observer()
        # self.observer.schedule(self.FileEventHandler(self), self.config['app']['notebook'], recursive=True)
        # self.observer.start()
        # 创建基本界面
        self.plate.title("ArtNote" + " - DEBUG" if DEBUG else "")
        self.frame.pack()
        self.file_tree.pack(side='left', fill='both')
        self.scrollbal1.pack(side='right', fill='y')
        self.scrollbal1['command'] = self.file_tree.yview
        self.file_tree['yscrollcommand'] = self.scrollbal1.set
        # 菜单渲染
        self.menubar = tk.Menu(tearoff=False)
        self.plugin_menu = None
        self.wenjian_menu = tk.Menu(tearoff=False)
        self.wenjian_menu.add('command', label='新建', command=lambda x='新建': menu_cmd(x))
        self.wenjian_menu.add('command', label='打开文件夹', command=lambda x='打开文件夹': menu_cmd(x))
        self.wenjian_menu.add('command', label='同步', command=lambda x='同步': menu_cmd(x))
        self.wenjian_menu.add('command', label='关于', command=lambda x='关于': menu_cmd(x))
        self.wenjian_menu.add('command', label='退出', command=lambda x='退出': menu_cmd(x))
        self.shitu_menu = tk.Menu(tearoff=False)
        self.shitu_menu.add('command', label='刷新', command=lambda x='刷新': menu_cmd(x))
        self.shitu_menu.add('checkbutton', label='置顶', command=lambda x='置顶': menu_cmd(x), variable=self.IsTop)
        self.menubar.add_cascade(label='文件', menu=self.wenjian_menu)
        self.menubar.add_cascade(label='视图', menu=self.shitu_menu)
        self.plate['menu'] = self.menubar
        # 加载文件解释器
        self.extension2view = dict()
        for filename in os.listdir("views"):
            if filename.endswith('.py') and not filename.startswith('__'):
                module_name = filename[:-3]
                module = import_module(f'views.{module_name}')
                for attr_name in dir(module):
                    attr = getattr(module, attr_name)
                    if isinstance(attr, type) and issubclass(attr, BaseView) and attr != BaseView:
                        plugin_instance = attr(self.unionview, self)
                        print("loading", attr_name, "for", plugin_instance.file_extension())
                        if plugin_instance.file_extension() in self.config["plugin"]:
                            plugin_instance.set_config(self.config["plugin"][plugin_instance.file_extension()])
                        else:
                            self.config["plugin"][plugin_instance.file_extension()] = plugin_instance.get_basic_config()
                        self.extension2view[plugin_instance.file_extension()] = plugin_instance
                        self.unionview.add(plugin_instance)
                        if plugin_instance.is_plugin():
                            if self.plugin_menu is None:
                                self.plugin_menu = tk.Menu(tearoff=False)
                                self.menubar.add_cascade(label='插件', menu=self.plugin_menu)
                            self.plugin_menu.add('command', label=attr_name,
                                                 command=lambda x=plugin_instance.file_extension(): self.unionview.select(self.extension2view[x]))
        self.unionview.pack(side="right", fill="y")

        self.pan.pack(fill="both", expand=1)
        self.pan.add(self.frame)
        self.pan.add(self.unionview)

        self.controlpad.pack(side='bottom', fill='x')

        # 渲染界面事件
        self.pan.bind('<ButtonRelease-1>', self.move_tree)

        # 设置程序缩放
        ctypes.windll.shcore.SetProcessDpiAwareness(1)
        self.plate.tk.call('tk', 'scaling', ctypes.windll.shcore.GetScaleFactorForDevice(0) / 75)
        # 渲染操作事件
        self.file_tree.bind('<ButtonRelease-1>', self.show_file)
        # 其他
        self.tree_of_now = 0  # 当前选中的TreeView组件的元素索引。
        self.drawtree(self.config['app']['notebook'])

    def show_file(self, event):
        tree_of_now = self.file_tree.focus()  # 返回当前选中的值，无则为空字符串。
        if tree_of_now == '':
            return
        self.tree_of_now = tree_of_now
        file_path = self.tree_path[tree_of_now]
        _, file_name = os.path.split(file_path)
        if file_name[0] == '#':
            file_name = Tookits.FileToolkits.decode_name(file_name[1:])
        self.file_path = file_path  # 绝对路径
        self.file_name = file_name  # 文件名
        self.plate.title(file_name)
        if tab_name := self.unionview.select():
            view = self.unionview.nametowidget(tab_name)
        if os.path.isfile(file_path):
            suffix = Tookits.FileToolkits.get_suffix_from_filename(file_name)
            if suffix in self.extension2view:
                self.unionview.select(self.extension2view[suffix])
                self.extension2view[suffix].open(file_path)
            else:
                print("不受支持的文件格式。")
        elif os.path.isdir(file_path):
            file_path = os.path.join(file_path, FOLDER_FILE)
            if not os.path.isfile(file_path):
                with open(file_path, mode='w'):
                    pass
            self.unionview.select(self.extension2view[".txt"])
            self.extension2view[".txt"].open(file_path)
            # folder open/close-state record
            if self.file_tree.item(tree_of_now, option='open'):
                if file_name not in self.openedfile:
                    self.openedfile.append(file_name)
            else:
                if file_name in self.openedfile:
                    self.openedfile.remove(file_name)
        else:
            print("文件不存在。")

    def on_closing(self):
        self.update()
        if not DEBUG:
            try:
                input("按任意键退出...")
            except EOFError:
                pass
        self.quit()

    def run(self):
        self.plate.mainloop()
        self.save()

    def save(self):
        with open(self.config_path, mode="w", encoding="utf-8") as file:
            json.dump(self.config, file, ensure_ascii=False, indent=4)

    def quit(self):
        # self.observer.stop()
        # self.observer.join()
        self.plate.quit()

    # 以下为内部方法，不建议调用。
    def check_update(self):
        tag, title, body, url = Tookits.VersionToolkits.getlatestversion()
        if Version(__Version__) < Version(tag):
            print("有新的版本可供安装（%s --> %s）" % (__Version__, tag))
            print(title)
            print(body)
            isdownload = tkm.askokcancel(title=title, message=body + "\n是否现在安装？")
            if isdownload:
                print("正在下载...")
                print(url)
                file = open("../update.bat", mode='w')
                file.write("move art-note art-note-%s" % __Version__)
                file.write("git clone https://gitee.com/ysu-xzh/art-note.git")
                file.write("pause")
                print("执行", os.path.abspath("../update.bat"), "即可完成程序更新")
                file.close()


    def move_tree(self, e):
        if self.IsHideTree.get():
            return
        self.scrollbal1.pack(side='right', fill='y')
        self.file_tree.pack(side='left', fill='both', expand=1)

    def drawtree(self, path_to_chase):
        Q = ' '
        filename = os.path.basename(path_to_chase)
        if Tookits.FileToolkits.get_suffix_from_filename(filename) in self.ignoresufix:
            return
        if os.path.isdir(path_to_chase):
            if filename[0] == '.':
                return
            if self.img_folder:
                Q = self.file_tree.insert(self.father_of_now[-1], self.tree_num[-1], text=filename,
                                          image=self.img_folder, open=(filename in self.openedfile),
                                          tags="font")
            else:
                Q = self.file_tree.insert(self.father_of_now[-1], self.tree_num[-1], text=filename,
                                          open=(filename in self.openedfile),
                                          tags="font")
            for i in os.listdir(path_to_chase):
                self.father_of_now.append(Q)
                self.tree_num.append(0)
                self.drawtree(os.path.join(path_to_chase, i))
                self.father_of_now.pop()
                self.tree_num.pop()
        elif os.path.isfile(path_to_chase):
            suffix = Tookits.FileToolkits.get_suffix_from_filename(filename)
            if filename[0] == '.' or suffix not in support_file_list:
                return
            if self.img_file:
                Q = self.file_tree.insert(self.father_of_now[-1], self.tree_num[-1], text=filename, image=self.img_file,
                                          tags="font")
            else:
                Q = self.file_tree.insert(self.father_of_now[-1], self.tree_num[-1], text=filename, tags="font")
        if Q != ' ':
            self.tree_path[Q] = path_to_chase

    # --- 插件方法
    def update(self):
        print("uploading...")
        print(self.git.upload(verbose=True))


def menu_cmd(x):
    file_path = app.file_path
    if x == '新建':
        name = tksd.askstring('输入子级目录名', '新建节点')
        if name is None:
            return
        if name != ''.join([x for x in name if x not in r'\/?"*<>|:']):
            name = '#' + Tookits.FileToolkits.encode_name(name)
        if os.path.isdir(file_path):
            file_name = os.path.join(file_path, name)
            open(file_name, mode='w', encoding=app.config['app']['encoding']).close()
            Q = app.file_tree.insert(app.tree_of_now, 0, text=name, valu=file_name, image=app.img_folder)
            app.tree_path[Q] = file_name

        if os.path.isfile(file_path):
            fr = open(file_path, mode='r', encoding=app.config['app']['encoding']).read()
            os.remove(file_path)
            if not os.path.isdir(file_path):
                os.makedirs(file_path)
                Q = app.file_tree.insert(app.tree_of_now, 0, text=name, image=app.img_file)
                app.tree_path[Q] = os.path.join(file_path, name)
            open(os.path.join(file_path, FOLDER_FILE), mode='w', encoding=app.config['app']['encoding']).write(fr)
            open(os.path.join(file_path, name), mode='w', encoding=app.config['app']['encoding']).close()
    elif x == '打开文件夹':
        if sys.platform == 'win32':
            os.system(f'start explorer "{file_path}"')
        else:
            print(f"未实现的细节。请将以下代码提交到issue。\n{__URL__}\n在{sys.platform}平台上的对文件夹的外部程序打开。")
    elif x == "同步":
        app.update()
    elif x == "关于":
        tkm.showinfo("关于", "发布日期：%s\n版本号：%s" % (__Data__, __Version__))
    elif x == '退出':
        app.quit()
    if x == '刷新':
        for item in app.file_tree.get_children():
            app.file_tree.delete(item)
        app.drawtree(app.config['app']['notebook'])
    elif x == '置顶':
        if app.IsTop.get() == 1:
            app.plate.wm_attributes('-topmost', True)
        else:
            app.plate.wm_attributes('-topmost', False)


if __name__ == '__main__':
    app = Application(config_path=config_file)
    app.run()